package ReflectDemo;

import java.lang.reflect.Field;
import java.lang.reflect.Method;


//java 反射
//反射就是把Java类中的各个成分映射成一个个的Java对象。
//即在运行状态中，对于任意一个类，都能够知道这个类的所以属性和方法；对于任意一个对象，都能调用它的任意一个方法和属性。
//这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。

//1. 反射机制的功能
//Java反射机制主要提供了以下功能：
//在运行时判断任意一个对象所属的类。
//在运行时构造任意一个类的对象。
//在运行时判断任意一个类所具有的成员变量和方法。
//在运行时调用任意一个对象的方法。
//生成动态代理。
//2. 实现反射机制的类
//Java中主要由以下的类来实现Java反射机制（这些类都位于java.lang.reflect包中）：
//Class类：代表一个类。 Field类：代表类的成员变量（成员变量也称为类的属性）。
//Method类：代表类的方法。
//Constructor类：代表类的构造方法。
//Array类：提供了动态创建数组，以及访问数组的元素的静态方法。

public class ReflectDemo {

    //用来创建一个和参数objcet同样类型的对象，然后把object对象中的所有属性拷贝到新建的对象中，并将其返回。
    public static Object test(Object object) throws Exception {
        //获得对象的类型
        Class classType = object.getClass();
        System.out.println("Class:" + classType.getName());

        //通过默认构造方法创建一个新的对象
        Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

        //获得对象的所有属性
        Field fields[] = classType.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];

            String fieldName = field.getName();
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            //获得和属性对应的getXXX()方法的名字
            String getMethodName = "get" + firstLetter + fieldName.substring(1);
            //获得和属性对应的setXXX()方法的名字
            String setMethodName = "set" + firstLetter + fieldName.substring(1);

            //获得和属性对应的getXXX()方法
            Method getMethod = classType.getMethod(getMethodName, new Class[]{});
            //获得和属性对应的setXXX()方法
            Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});

            //调用原对象的getXXX()方法
            Object value = getMethod.invoke(object, new Object[]{});
            System.out.println(fieldName + ":" + value);
            //调用拷贝对象的setXXX()方法
            setMethod.invoke(objectCopy, new Object[]{value});
        }
        return objectCopy;
    }

    //下面分析一下上述代码。
    //首先，通过Object类中的getClass()方法获取对象的类型。
    //Class classType=object.getClass(); 而Class类是Reflection API中的核心类，主要方法如下：
    //getName()：获得类的完整名字。 getFields()：获得类的public类型的属性。
    //getDeclaredFields()：获得类的所有属性。
    //getMethods()：获得类的public类型的方法。
    //getDeclaredMethods()：获得类的所有方法。
    //getMethod(String name, Class[] parameterTypes)：获得类的特定方法，name参数指定方法的名字，parameterTypes参数指定方法的参数类型。
    //getConstrutors()：获得类的public类型的构造方法。
    //getConstrutor(Class[] parameterTypes)：获得类的特定构造方法，parameterTypes参数指定构造方法的参数类型。
    //newInstance()：通过类的不带参数的构造方法创建这个类的一个对象。
    //第二步，通过默认构造方法创建一个新的对象，即先调用Class类的getConstructor()方法获得一个Constructor对象，它代表默认的构造方法，
    //然后调用Constructor对象的newInstance()方法构造一个实例。
    //Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
    //第三步，获得对象的所有属性，即通过Class类的getDeclaredFields()方法返回类的所有属性，包括public、protected、default和private访问级别的属性，
    //Field fields[]=classType.getDeclaredFields();
    //第四步，获得每个属性相应的get/set方法，然后执行这些方法，把原来的对象属性拷贝到新的对象中。
    //这里我们可以写一个InvokeTester的类，然后运用反射机制调用一个InvokeTester对象的add()方法（自定义方法），
    //如add()方法的两个参数为int类型，那么获取表示add()方法的Method对象代码如下：
    //Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
    //上述代码中也有用到Method的invoke方法，其接收参数必须为对象，如果参数为基本数据类型，必须转换为相应的包装类型的对象，
    //如int要转换为Integer。而invoke方法的返回值总是对象，如果实际被调用的方法的返回类型是基本数据类型，那么invoke方法会将其转换为相应的包装类型的对象，再将其返回。

    private void test2() throws Exception{
        Customer customer = new Customer();
        customer.setId(10L);
        customer.setName("adam");
        customer.setAge(3);
        ReflectDemo.test(customer);
    }

    public static void main(String[] args) {
        try {
            new ReflectDemo().test2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public class Customer {
        private Long id;
        private String name;
        private int age;

        public Customer() {
        }

        public Customer(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}



